package mspbots.next.ticket.core.grader.interceptor;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import mspbots.next.ticket.core.grader.AbstractGradeRuleAdapter;
import mspbots.next.ticket.core.grader.GradeRuleInterceptorRegistration;
import mspbots.next.ticket.core.points.PointResult;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;
import org.springframework.data.relational.core.query.Criteria;
import org.springframework.data.relational.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author Jay Yang
 */
@Log4j2
@Service(value = "COMPANY_STATUS_POINT_RULE")
@RequiredArgsConstructor
public class CompanyStatusRuleInterceptor extends AbstractGradeRuleAdapter {

    private static final String RULE_KEY = "CompanyStatus";
    private final Map<Long, Companies> companyMap = new Hashtable<>(100);
    private List<CompanyStatusParam> params;

    @Override
    public Mono<GradeRuleInterceptorRegistration> prepare(GradeRuleInterceptorRegistration gradeRuleInterceptorRegistration) {
        var mono1 = this.r2dbcEntityTemplate.select(Companies.class)
                .matching(Query.query(Criteria.where("tenantCode")
                        .is(gradeRuleInterceptorRegistration.getRule().getTenantId())))
                .all()
                .doOnNext(re -> this.companyMap.put(re.getCompanyId(), re));
        return Flux.concatDelayError(mono1)
                .doFinally(r -> this.params = super.objectMapper
                        .convertValue(gradeRuleInterceptorRegistration.getRule().getParams(),
                                new TypeReference<>() {
                                }))
                .then(Mono.just(gradeRuleInterceptorRegistration));
    }

    @Override
    public Mono<PointResult> scoring(JsonNode ticket) {
        AtomicInteger order = new AtomicInteger();
        Long companyId = super.getLongValueId(ticket.get("company"));
        String companyName = super.getStringValueName(ticket.get("company"));
        String statusName = "";
        if (!ObjectUtils.isEmpty(companyId) && companyMap.containsKey(companyId)) {
            Companies company = companyMap.get(companyId);
            statusName = company.getCompanyStatusName();
            params.stream().filter(p -> p.isSelected() && p.getCompanyStatusIds().contains(company.getCompanyStatusId()))
                    .forEach(item -> order.addAndGet(item.getPoints()));
        }
        return Mono.just(PointResult.withDefault().point(order.get())
                .detail(PointResult.Detail.with(order.get(), Map.of("companyName", companyName, "companyStatus", statusName), RULE_KEY)));
    }

    @Data
    private static class CompanyStatusParam {
        private Integer points;
        private boolean selected;
        private Integer index;
        private List<Integer> companyStatusIds;
    }

    @Data
    @Table("cw_companies_view")
    private static class Companies {
        @Id
        private Long id;
        private Long companyId;
        private String companyName;
        private Integer companyStatusId;
        private String companyStatusName;
        private Long tenantCode;
    }
}
